package Dm

import (
	"fmt"
	"db2s/ETL"
	"db2s/global"
	"runtime"
	"sort"
	"strings"
)

type TableActive struct {
}

func (or TableActive) Truncate(s global.TableActiveInput) (err error) {
	var (
		event       string
		execTimeout int64
		tableName   string
		truncateSql []*string
	)
	vlog = fmt.Sprintf("(%d) %v Start executing the truncate action of table %v.%v", s.LogSeq, event, s.TableInfo.Schema, s.TableInfo.Table)
	WLog.Debug(vlog)
	tableName = fmt.Sprintf("\"%v\".\"%v\"", s.TableInfo.Schema, s.TableInfo.Table)
	if len(s.Input.Partition.PartitionGather) > 0 {
		for _, v := range s.Input.Partition.PartitionGather {
			if strings.EqualFold(v, "single") {
				sql := fmt.Sprintf("truncate table %v", tableName)
				truncateSql = append(truncateSql, &sql)
			} else {
				if len(v) > 0 {
					sql := fmt.Sprintf("ALTER TABLE %v TRUNCATE PARTITION %v", tableName, v)
					truncateSql = append(truncateSql, &sql)
				}
			}
		}
	} else {
		sql := fmt.Sprintf("truncate table %v", tableName)
		truncateSql = append(truncateSql, &sql)
	}
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.DB["single"], StopTime: s.Input.SqlExecStopTime, SqlLogBin: s.Optimizer.SqlLogBin, SqlMode: true}
	if execTimeout, err = active.TrxUpdate(truncateSql); err != nil {

	}
	if _, err = active.TrxUpdate(truncateSql); err != nil {
		vlog = fmt.Sprintf("(%d) %v The execution of a sql statement failed. table is %v.%v,error is {%v}", s.LogSeq, event, s.TableInfo.Schema, s.TableInfo.Table, err)
		WLog.Error(vlog)
		return
	}
	vlog = fmt.Sprintf("(%d) %v A sql statement was executed successfully. table is %v.%v, exec timeout is {%vms}", s.LogSeq, event, s.TableInfo.Schema, s.TableInfo.Table, execTimeout)
	WLog.Debug(vlog)
	return
}

// Analyze analyze table 收集统计信息
func (or TableActive) Analyze(_ global.TableActiveInput) (any, error) {
	var (
		//Event       = "[ApplyAnalyze]"
		execTimeout int64
	)
	//pc, _, _, _ := runtime.Caller(1)
	//callingFunc := runtime.FuncForPC(pc)
	//callFile, callLine := callingFunc.FileLine(pc)
	//callFunc := callingFunc.Name()
	//callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	//vlog = fmt.Sprintf("(%d) %v %v Table %v.%v starts executing the analyze table statement", s1.LogSeq, callFuncInfo, Event, or.Schema, or.Table)
	//WLog.Debug(vlog)
	//ShardName = "single"
	//if !or.SqlMode {
	//	setVariables = append(setVariables, "set session sql_mode=''")
	//}
	//dispos := dataDispos.DBdataDispos{SessionSet: setVariables, DB: s1.DB[ShardName], SqlLogBin: or.SqlLogBin}
	/*
		orsql 单机bug，lower_case_table_names = 1 , use schema xxx ,执行analyze table 表名为大写时，不收集统计信息，只能改为小写
	*/
	//analyzeTable := fmt.Sprintf("analyze table `%s`", strings.ToLower(or.Table))
	//use := fmt.Sprintf("use `%s`", or.Schema)
	//sqlstr := []*string{&use, &analyzeTable}
	//if execTimeout, err = dispos.BeginTrxUpdate(sqlstr); err != nil {
	//	vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s1.LogSeq, callFuncInfo, Event, sqlstr, err)
	//	WLog.Error(vlog)
	//	return nil, err
	//}
	//vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s1.LogSeq, callFuncInfo, Event, sqlstr, execTimeout)
	//WLog.Debug(vlog)
	return execTimeout, nil
}

type keyValuePair struct {
	key   string
	value int64
}

// OptimizerOrderBy 同一组内多表进行数据操作时，针对表做排序，按照最优排序
func (or TableActive) OptimizerOrderBy(s global.TableActiveInput) (any, error) {
	// 将 map 转换为 slice 类型
	var (
		pairs           []keyValuePair
		tableOrderBySeq []string
	)
	// --按照rows arv length进行排序
	for k, v := range s.OptimizerOrderBy.SrcArgRowsLength {
		pairs = append(pairs, keyValuePair{k, v.(int64)})
	}
	// 对 slice 进行排序,根据 int64 值进行排序
	sort.Slice(pairs, func(i, j int) bool {
		return pairs[i].value < pairs[j].value
	})
	// -- 按照目标端shard轮询的方式进行拆分
	for _, v := range pairs {
		tableOrderBySeq = append(tableOrderBySeq, v.key)
	}
	return tableOrderBySeq, nil
}

// Alter 操作表的alter语句
func (or TableActive) Alter(_ global.TableActiveInput) (any, error) {
	return nil, nil
}

// CreateDatabase 收集统计信息
func (or TableActive) CreateDatabase(s global.TableActiveInput) (any, error) {
	var (
		setVariables = []string{"SET session unique_checks=0", "SET session foreign_key_checks=0"}
		event        = "[CreateDatabase]"
		execTimeout  int64
		shardName    string
		err          error
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Table %v.%v starts executing the analyze table statement", s.LogSeq, callFuncInfo, event, s.TableInfo.Schema, s.TableInfo.Table)
	WLog.Debug(vlog)
	shardName = "single"
	if !s.Optimizer.SqlMode {
		setVariables = append(setVariables, "set session sql_mode=''")
	}
	active := ETL.InstanceActiveS{SessionSet: setVariables, DB: s.DB[shardName], SqlLogBin: s.Input.SqlLogBin, SqlMode: true}
	/*
		mysql 单机bug，lower_case_table_names = 1 , use schema xxx ,执行analyze table 表名为大写时，不收集统计信息，只能改为小写
	*/
	createDatabase := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", s.TableInfo.Schema)
	sqlStr := []*string{&createDatabase}
	if execTimeout, err = active.TrxUpdate(sqlStr); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, sqlStr, err)
		WLog.Error(vlog)
		return nil, err
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, sqlStr, execTimeout)
	WLog.Debug(vlog)
	return execTimeout, nil
}

// Create 收集统计信息
func (or TableActive) Create(s global.TableActiveInput) (any, error) {
	var (
		setVariables = []string{"SET session unique_checks=0", "SET session foreign_key_checks=0"}
		execTimeout  int64
		shardName    string
		err          error
	)
	shardName = "single"
	if !s.Optimizer.SqlMode {
		setVariables = append(setVariables, "set session sql_mode=''")
	}
	active := ETL.InstanceActiveS{SessionSet: setVariables, DB: s.DB[shardName], SqlLogBin: s.Input.SqlLogBin, SqlMode: true}
	sqlStr := []*string{&s.Create.ExecSql}
	if execTimeout, err = active.DDL(sqlStr); err != nil {
		return nil, err
	}
	return execTimeout, nil
}

// Drop 收集统计信息
func (or TableActive) Drop(s global.TableActiveInput) (res any, err error) {
	var (
		event       = "[drop]"
		execTimeout int64
	)
	if execTimeout, _, err = ExecUpdateSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		UpdateSqlStr: s.Drop.ExecSql,
		ShardName:    "single",
	}); err != nil {
		return
	}
	return execTimeout, nil
}

// Recover 收集统计信息
func (or TableActive) Recover(s global.TableActiveInput) (any, error) {
	var (
		setVariables = []string{"SET session unique_checks=0", "SET session foreign_key_checks=0"}
		event        = "[Recover]"
		execTimeout  int64
		shardName    string
		err          error
	)
	pc, _, _, _ := runtime.Caller(1)
	callingFunc := runtime.FuncForPC(pc)
	callFile, callLine := callingFunc.FileLine(pc)
	callFunc := callingFunc.Name()
	callFuncInfo := fmt.Sprintf("Call functions %v Call File %v:%v", callFunc, callFile, callLine)
	vlog = fmt.Sprintf("(%d) %v %v Table %v.%v starts executing the analyze table statement", s.LogSeq, callFuncInfo, event, s.TableInfo.Schema, s.TableInfo.Table)
	WLog.Debug(vlog)
	shardName = "single"
	if !s.Optimizer.SqlMode {
		setVariables = append(setVariables, "set session sql_mode=''")
	}
	active := ETL.InstanceActiveS{SessionSet: setVariables, DB: s.DB[shardName], SqlLogBin: s.Input.SqlLogBin, SqlMode: true}
	renameTable := fmt.Sprintf("rename table `%v`.`%v` to `%v`.`%v` ", s.TableInfo.Schema, s.TableInfo.Table, s.TableInfo.RecoverDbName, s.TableInfo.RecoverTableName)
	createTable := fmt.Sprintf("create table `%v`.`%v` like `%v`.`%v` ", s.TableInfo.Schema, s.TableInfo.Table, s.TableInfo.RecoverDbName, s.TableInfo.RecoverTableName)
	sqlStr := []*string{&renameTable, &createTable}
	if execTimeout, err = active.TrxUpdate(sqlStr); err != nil {
		vlog = fmt.Sprintf("(%d) %v %v The execution of a sql statement failed. sql is {%v},error is {%v}", s.LogSeq, callFuncInfo, event, sqlStr, err)
		WLog.Error(vlog)
		return false, err
	}
	vlog = fmt.Sprintf("(%d) %v %v A sql statement was executed successfully. sql is {%v}, exec timeout is {%vms}", s.LogSeq, callFuncInfo, event, sqlStr, execTimeout)
	WLog.Debug(vlog)
	return execTimeout, nil
}
